#include "SDL.h"
#include "SDL_gfxPrimitives.h"
#include "mandelbrot.h"
#include <iostream>
using namespace std;

static int screen_width = 1680;
static int screen_height = 1050;
static double offset_x = static_cast<double>(screen_width) / 2. ; 
static double offset_y = static_cast<double>(screen_height) / 2. ;
static double scale_x = 4.2;
static double scale_y = 2.625;
static bool animate = true;
static SDL_Rect zone_select = {0, 0, 0, 0};

static SDL_Surface* screen = NULL;
static Uint8* palette_r = NULL;
static Uint8* palette_g = NULL;
static Uint8* palette_b = NULL;

static int niter = 32;

static void putpixel (Uint16 x, Uint16 y,
                      Uint8 r, Uint8 g, Uint8 b) {
  pixelRGBA(screen,x,y,r,g,b,255);
}

static void gen_palette () {
  palette_r = new Uint8[niter];
  palette_g = new Uint8[niter];
  palette_b = new Uint8[niter];

  double step = 255. / static_cast<double>(niter);

  for (int k = 0; k < niter; ++k) {
    palette_r[k] = static_cast<double>(k)*step;
    palette_g[k] = 0;
    palette_b[k] = 100;
    /*palette_r[k] = static_cast<double>(k)*step;
    palette_g[k] = 128-palette_r[k]/2.;
    palette_b[k] = 255-palette_r[k];*/
    /*palette_r[k] = rand() % 256;
    palette_g[k] = rand() % 256;
    palette_b[k] = rand() % 256;*/
  }
}

static void render_mandelbrot () {
  bool done = false;

  double lscale_x = screen_width / scale_x;
  double lscale_y = screen_height / scale_y;

  int kniter = (animate) ? (1) : (niter-1);

  for (; (!done) && (kniter < niter); ++kniter) {
    for (double y = 0; y < screen_height; y+=1.) {
      for (double x = 0; x < screen_width; x+=1.) {
        int nsteps = 0;
        if (in_mandelbrot_set((x-offset_x)/lscale_x, (y-offset_y)/lscale_y,
              nsteps,kniter)) {
          putpixel(x,y,0,0,0);
        }
        else {
          putpixel(x,y,palette_r[nsteps],palette_g[nsteps],palette_b[nsteps]);
        }
      }
    }

    SDL_UpdateRect(screen,0,0,screen_width,screen_height);
    SDL_Delay(2);

    SDL_Event evt;
    while (SDL_PollEvent(&evt) > 0) {
      if ((evt.type == SDL_KEYDOWN) &&
          (evt.key.keysym.sym == SDLK_ESCAPE)) {
        done = true;
        break;
      }
    }
  }
}

static void catch_events () {
  SDL_Event evt;
  bool done = false;

  while (!done) {
    while (!done && (SDL_PollEvent(&evt) > 0)) {
      switch (evt.type) {
        case SDL_QUIT:
          done = true;
          break;
        case SDL_MOUSEBUTTONDOWN:
          zone_select.x = evt.motion.x;
          zone_select.y = evt.motion.y;
          break;
        case SDL_MOUSEBUTTONUP:
        {
          Uint16 real_startx = (evt.motion.x < zone_select.x) ?
                              (evt.motion.x) : (zone_select.x);
          Uint16 real_starty = (evt.motion.y < zone_select.y) ?
                              (evt.motion.y) : (zone_select.y);
          Sint16 width = evt.motion.x - zone_select.x;
          Sint16 height = evt.motion.y - zone_select.y;
          if (width < 0) { width *= -1; }
          if (height < 0) { height *= -1; }
          offset_x = real_startx + static_cast<double>(screen_width) / 2. ;// - real_start;
          offset_y = real_starty + static_cast<double>(screen_height) / 2. ;//- real_end;
          scale_x /= (static_cast<double>(screen_width) /
                      static_cast<double>(width));
          scale_y /= (static_cast<double>(screen_height) /
                      static_cast<double>(height));
          render_mandelbrot();
          break;
        }
        case SDL_KEYDOWN:
          switch (evt.key.keysym.sym) {
            case SDLK_ESCAPE:
              done = true;
              break;
            case SDLK_PLUS:
            case SDLK_KP_PLUS:
            case SDLK_p:
              scale_x /= 1.3 ;
              scale_y /= 1.3 ;
              niter << 1;
              render_mandelbrot();
              break;
            case SDLK_a:
              animate = (animate) ? (false) : (true);
              break;
            case SDLK_MINUS:
            case SDLK_KP_MINUS:
            case SDLK_m:
              scale_x *= 1.3 ;
              scale_y *= 1.3 ;
              niter >> 1;
              render_mandelbrot();
              break;
            case SDLK_LEFT:
              offset_x += static_cast<double>(screen_width) / 50. ;
              render_mandelbrot();
              break;
            case SDLK_RIGHT:
              offset_x -= static_cast<double>(screen_width) / 50. ;
              render_mandelbrot();
              break;
            case SDLK_UP:
              offset_y += static_cast<double>(screen_height) / 100. ;
              render_mandelbrot();
              break;
            case SDLK_DOWN:
              offset_y -= static_cast<double>(screen_height) / 100. ;
              render_mandelbrot();
              break;
            default:
              while(false);
          }
          break;
        default:
          while(false);
      } // End switch event type
    } // End poll events
  } // End event loop
}

int main (int argc, char** argv) {

  srand(time(0));

  if (argc < 2) {
    cerr << "Error: syntax is: " << argv[0] << " niter" << endl;
    return EXIT_FAILURE;
  }

  niter = atoi(argv[1]);
  
  SDL_Init(SDL_INIT_VIDEO);
  screen =
    SDL_SetVideoMode(screen_width,screen_height,32,SDL_ANYFORMAT|SDL_FULLSCREEN);

  SDL_EnableKeyRepeat(250, 0);

  gen_palette ();

  // Render the first shot
  render_mandelbrot();
  // Catch event and update the mandelbrot shot
  // if necessary.
  catch_events ();

  delete [] palette_r;
  delete [] palette_g;
  delete [] palette_b;

  SDL_Quit();
  return EXIT_SUCCESS;
}

